/**
 * 2009 # 22/08/2009 # 17:07:33
 * pratico
 */
package br.org.bertol.mestrado.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import br.org.bertol.mestrado.engine.fitness.Objective;
import br.org.bertol.mestrado.engine.optimisation.entity.StoreResult;

/**
 * @author contaqualquer
 */
public class Report<T> {

    /***/
    private String                                      systemID;

    /** Marca início da execução do processso. */
    private long                                        initTime;

    /** Marca fim da execução do processso. */
    private long                                        stopTime;

    /***/
    private final transient int                         MINUTE_CONVERTER = 60000;

    /***/
    private transient List<List<StoreResult<T>>>        consolidateResult;

    /***/
    private transient Objective[]                       objectives;

    /***/
    private transient HashMap<String, ? extends Number> map;

    /**
     * @param map
     *            the map to map
     */
    public final void setSet(final HashMap<String, ? extends Number> map) {
        this.map = map;
    }

    /**
     * @param objectives
     *            the objectives to set
     */
    public final void setObjectives(final Objective[] objectives) {
        this.objectives = objectives;
    }

    /**
     * @param systemID
     *            the systemID to set
     */
    public final void setSystemID(final String systemID) {
        this.systemID = systemID;
    }

    /**
     * @param initTime
     *            the initTime to set
     */
    public final void setInitTime(final long initTime) {
        this.initTime = initTime;
    }

    /**
     * @param stopTime
     *            the stopTime to set
     */
    public final void setStopTime(final long stopTime) {
        this.stopTime = stopTime;
    }

    /**
     * @param consolidateResult
     *            the consolidateResult to set
     */
    public final void setConsolidateResult(
            final List<List<StoreResult<T>>> consolidateResult) {
        this.consolidateResult = consolidateResult;
    }

    /**
     * Salva arquivo em formato TXT para análise.
     * @param outWriter
     *            Arquivo de saída
     */
    public final void storeResultAnalisis(final PrintWriter outWriter) {

        for (final List<StoreResult<T>> storeResults : consolidateResult) {

            for (final StoreResult<?> storeResult : storeResults) {

                for (final Objective objective : objectives) {

                    outWriter.print(storeResult.getHeightString()
                            .get(objective));

                    outWriter.print(" ");
                }

                outWriter.println();

            }

        }

        outWriter.println();

    }

    /**
     * Salva o resultado em planilha excel.
     * @param workbook
     *            Arquivo excel
     */
    public final void getResultSheet(final Workbook workbook) {

        int rowCounter = 0;

        // cria fonte cabeçaçho
        final Font fontHeader = workbook.createFont();
        fontHeader.setFontHeightInPoints(Short.valueOf("10"));
        fontHeader.setBoldweight(Font.BOLDWEIGHT_NORMAL);
        fontHeader.setColor(IndexedColors.WHITE.getIndex());

        // estilo do cabeçalho
        final CellStyle styleHeader =
                getCellLayout(workbook,
                              IndexedColors.GREEN,
                              IndexedColors.WHITE,
                              Font.BOLDWEIGHT_NORMAL);

        // estilo do corpo
        final CellStyle styleBody =
                getCellLayout(workbook,
                              IndexedColors.WHITE,
                              IndexedColors.AUTOMATIC,
                              Font.BOLDWEIGHT_NORMAL);

        // cria fonte rodapé
        final CellStyle styleFooter =
                getCellLayout(
                              workbook,
                              IndexedColors.GREY_50_PERCENT,
                              IndexedColors.WHITE,
                              Font.BOLDWEIGHT_NORMAL);

        Sheet sheet = workbook.getSheet(systemID);

        if (sheet == null) {
            sheet = workbook.createSheet(systemID);

        } else {

            rowCounter = sheet.getLastRowNum() + 2;
        }

        for (final List<StoreResult<T>> storeResults : consolidateResult) {

            final Row header = sheet.createRow(rowCounter);

            int cellCouter = 0;

            header.createCell(cellCouter)
                    .setCellValue("Nº Mutantes");
            header.getCell(cellCouter).setCellStyle(styleHeader);
            cellCouter++;

            header.createCell(cellCouter)
                    .setCellValue("Cobertura");
            header.getCell(cellCouter).setCellStyle(styleHeader);
            cellCouter++;

            header.createCell(cellCouter)
                    .setCellValue("Operadores");
            header.getCell(cellCouter).setCellStyle(styleHeader);
            cellCouter++;

            header.createCell(cellCouter)
                    .setCellValue("Chamadas de fitness");
            header.getCell(cellCouter).setCellStyle(styleHeader);
            cellCouter++;

            header.createCell(cellCouter)
                    .setCellValue("Casos de teste");
            header.getCell(cellCouter).setCellStyle(styleHeader);

            // adiciona dados da execução
            for (final StoreResult<?> storeResult : storeResults) {

                cellCouter = 0;

                final Row row = sheet.createRow(++rowCounter);

                for (final Objective objective : objectives) {

                    final int cellNum = row.getLastCellNum() < 0 ? 0 : row
                            .getLastCellNum();

                    final Cell cellVal = row.createCell(cellNum);

                    cellVal
                            .setCellValue(storeResult.getHeightNumerical()
                            .get(objective).doubleValue());

                    cellVal.setCellStyle(styleBody);

                    cellVal.setCellType(Cell.CELL_TYPE_NUMERIC);

                    cellCouter++;

                }
                // operadores
                row.createCell(cellCouter)
                        .setCellValue(
                                      storeResult.getRoute());

                row.getCell(cellCouter).setCellStyle(styleBody);

                cellCouter++;

                // chamadas de fitness
                row.createCell(cellCouter)
                        .setCellValue(
                                      storeResult.getFitnessCounter());

                row.getCell(cellCouter).setCellStyle(styleBody);

                cellCouter++;

                // casos de teste
                row
                        .createCell(cellCouter)
                        .setCellValue(
                                      storeResult
                        .getAbstractCombinatorialFitness());

                row.getCell(cellCouter).setCellStyle(styleBody);
            }
        }

        // adiciona tempo de execução
        final Row timeFooter = sheet.createRow(++rowCounter);

        timeFooter.createCell(0)
                .setCellValue("Tempo de execução");

        timeFooter.getCell(0).setCellStyle(styleFooter);

        timeFooter.createCell(1)
                .setCellValue((stopTime - initTime) / MINUTE_CONVERTER);

        timeFooter.getCell(1).setCellType(Cell.CELL_TYPE_NUMERIC);

        timeFooter.getCell(1).setCellStyle(styleFooter);

        // adiciona outras informações
        for (Entry<String, ? extends Number> entry : map.entrySet()) {
            final Row miscFooter = sheet.createRow(++rowCounter);

            miscFooter.createCell(0)
                    .setCellValue(entry.getKey());

            miscFooter.getCell(0).setCellStyle(styleFooter);

            miscFooter.createCell(1)
                    .setCellValue(entry.getValue().doubleValue());

            miscFooter.getCell(1).setCellStyle(styleFooter);

            miscFooter.getCell(1).setCellType(Cell.CELL_TYPE_NUMERIC);

        }

    }

    /**
     * Cria estilo de layout para a célula.
     * @param workbook
     *            Arquivo a ser manipulado
     * @param foregroundColor
     *            Cor de preenchimento da célula
     * @param fontColor
     *            Cor da fonte
     * @param fontBold
     *            Negrito ou não
     * @return {@link CellStyle} Estilo de célula
     */
    private CellStyle getCellLayout(final Workbook workbook,
            final IndexedColors foregroundColor, final IndexedColors fontColor,
            final Short fontBold) {

        final CellStyle style = workbook.createCellStyle();

        final Font font = workbook.createFont();
        font.setFontHeightInPoints(Short.valueOf("10"));
        font.setBoldweight(fontBold);
        font.setColor(fontColor.getIndex());
        font.setFontName(Font.FONT_ARIAL);

        style.setFont(font);
        style.setFillForegroundColor(foregroundColor.getIndex());
        style.setFillPattern(CellStyle.SOLID_FOREGROUND);

        // ajusta bordas
        style.setBorderLeft(CellStyle.BORDER_THIN);
        style.setLeftBorderColor(IndexedColors.BLACK.getIndex());

        style.setBorderBottom(CellStyle.BORDER_THIN);
        style.setBottomBorderColor(IndexedColors.BLACK.getIndex());

        style.setBorderTop(CellStyle.BORDER_THIN);
        style.setTopBorderColor(IndexedColors.BLACK.getIndex());

        style.setBorderRight(CellStyle.BORDER_THIN);
        style.setRightBorderColor(IndexedColors.BLACK.getIndex());

        return style;

    }

    /**
     * Retorna o stream de entrada de um arquivo.
     * Caso o arquivo não exista ele é criado.
     * @param fileName
     *            Arquivo para as entrada
     * @return {@link Workbook} Instância da entrada
     * @throws IOException
     *             Erro ao abrir arquivo
     */
    public final Workbook getInputStream(final String fileName)
            throws IOException {

        final File file = new File(fileName);

        if (!file.exists()) {

            return new HSSFWorkbook();

        }

        return new HSSFWorkbook(new POIFSFileSystem(new FileInputStream(file)),
                true);

    }

}
